﻿using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;

namespace Asset
{
    public class Define
    {
        /// <summary>
        /// 编辑器下面资源的前缀
        /// </summary>
        public const string editorPre = "Assets/Res/";
        public const string assetUrl = "assetbundle/cfgs/AssetMap";
        public const string manifestName = "StreamingAssets";
        public static string abPre => Application.streamingAssetsPath;

    }

    public class PreLoadInfo
    {
        public int count;
        public string resName;
        public PreLoadInfo(string resName, int count)
        {
            this.count = count;
            this.resName = resName;
        }
    }

    public class PoolMgr : Singleton<PoolMgr>
    {
        #region 私有初始化
#if DEVELOP
        private Dictionary<string, string> editorPathPool = new Dictionary<string, string>();
#else
        private Dictionary<string, string> pool = new Dictionary<string, string>();
        private AssetBundleManifest assetBundleManifest = null;//初始化获取依赖
#endif

        private System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
        protected override void initialize()
        {
            Application.targetFrameRate = 60;
            Loader.Instance.init();
            PoolRoot.Instance.init();
        }

        public void init()
        {
            stopwatch.Start();
#if DEVELOP
            if (editorPathPool.Count > 0) return;
            loadEditorMap();
#else
            if (pool.Count > 0) return;
            loadManifest();
            loadAssetMap();
#endif
            stopwatch.Stop();
            UnityEngine.Debug.LogError(string.Format("初始化资源配置开销: {0} ms", stopwatch.ElapsedMilliseconds));
        }

#if DEVELOP
        private void loadEditorMap()
        {
            string path = "Assets/Res/AssetBundle/Cfgs/EditorAssetMap.asset";
            ScriptableObject obj = UnityEditor.AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
            EditorAssetCfg cfg = obj as EditorAssetCfg;
            for (int i = 0; i < cfg.assetNames.Count; i++)
            {
                string assetName = cfg.assetNames[i];
                string assetPath = cfg.assetPaths[i];
                editorPathPool.Add(assetName, assetPath);
            }
        }
#endif

#if !DEVELOP
        private void loadManifest()
        {
            //LoadFromFile不可以有.assetbundle后缀
            string path = Path.Combine(Define.abPre, Define.manifestName);
            AssetBundle bundle = AssetBundle.LoadFromFile(path);
            assetBundleManifest = bundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            // 压缩包释放掉
            bundle.Unload(false);
            bundle = null;
        }

        private string[] getAllDeps(string bundleName)
        {
            if (Instance.assetBundleManifest == null) return null;
            if (Instance.assetBundleManifest != null)
            {
                return Instance.assetBundleManifest.GetAllDependencies(bundleName);
            }
            return null;
        }

        private void loadAssetMap()
        {
            ScriptableObject obj = Asset.ResPool.Instance.loadAsset<UnityEngine.ScriptableObject>(Define.assetUrl.ToLower(), "AssetMap".ToLower());
            AssetCfg cfg = obj as AssetCfg;
            for (int i = 0; i < cfg.assetNames.Count; i++)
            {
                string assetName = cfg.assetNames[i];
                string bundleName = cfg.bundleNames[i];
                pool.Add(assetName, bundleName);
            }
        }
#endif
        public string getAssetUrl(string assetName)
        {
            string resUrl = null;
#if DEVELOP
            if (editorPathPool.TryGetValue(assetName, out resUrl))
            {
                return resUrl;
            }
#else
            if (pool.TryGetValue(assetName, out resUrl))
            {
                return resUrl;
            }
#endif
            return null;
        }
        #endregion

        #region 接口
        /// <summary>
        /// 异步加载Asset
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="resName"></param>
        /// <returns></returns>
        public static AsyncAsset<T> loadAssetAsync<T>(string resName) where T : UnityEngine.Object
        {
            resName = resName.ToLower();
            string resUrl = Instance.getAssetUrl(resName);
            if (resUrl != null)
            {
                return ResPool.Instance.loadAssetAsync<T>(resUrl, resName);
            }
            return null;
        }
        /// <summary>
        /// 同步加载Asset
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="resName"></param>
        /// <returns></returns>
        public static T loadAsset<T>(string resName) where T : UnityEngine.Object
        {
            resName = resName.ToLower();
            string resUrl = Instance.getAssetUrl(resName);
            if (resUrl != null)
            {
                return ResPool.Instance.loadAsset<T>(resUrl, resName);
            }
            return null;
        }
        /// <summary>
        /// 同步实例化
        /// </summary>
        /// <param name="resName"></param>
        /// <param name="poolType"></param>
        /// <returns></returns>
        public static GameObject instantiateObj(string resName, Pool_Type poolType = Pool_Type.None)
        {
            resName = resName.ToLower();
            string resUrl = Instance.getAssetUrl(resName);
            if (resUrl != null)
            {
                return ResPool.Instance.instantiateObj(resUrl, resName, poolType);
            }
            return null;
        }
        /// <summary>
        /// 异步实例化
        /// </summary>
        /// <param name="resName"></param>
        /// <param name="callBack"></param>
        /// <param name="poolType"></param>
        public static void instantiateObjAsync(string resName, Action<GameObject> callBack, Pool_Type poolType = Pool_Type.None)
        {
            resName = resName.ToLower();
            string resUrl = Instance.getAssetUrl(resName);
            if (resUrl != null)
            {
                ResPool.Instance.instantiateObjAsync(resUrl, resName, callBack, poolType);
            }
        }
        /// <summary>
        /// 中止实例化 针对异步实例化
        /// </summary>
        /// <param name="resName"></param>
        /// <param name="callBack"></param>
        public static void unloadObj(string resName, Action<GameObject> callBack)
        {
            resName = resName.ToLower();
            string resUrl = Instance.getAssetUrl(resName);
            if (resUrl != null)
            {
                ResPool.Instance.unloadObj(resUrl, resName, callBack);
            }
        }
        /// <summary>
        /// 回收GameObject
        /// </summary>
        /// <param name="obj"></param>
        public static void recyleObj(GameObject obj)
        {
            if (obj != null)
            {
                ResPool.Instance.recyleObj(obj);
            }
        }

        public static void addRef(string bundleName)
        {
            IAsyncBundle bundle = ResPool.Instance.getBundle(bundleName);
            if (bundle != null)
            {
                bundle.RefCount++;
            }
        }
        public static void subRef(string bundleName)
        {
            IAsyncBundle bundle = ResPool.Instance.getBundle(bundleName);
            if (bundle != null)
            {
                bundle.RefCount--;
            }
        }
        public static string getBundleName(string assetName)
        {
            assetName = assetName.ToLower();
            return Instance.getAssetUrl(assetName);
        }

        private List<PreLoadInfo> preLoad = null;
        private Action<int, string> loadSceneFinishHandler = null;
        private bool isInLoadingScene = false;
        private string sceneName = "";
        public string SceneName
        {
            get
            {
                return this.sceneName;
            }
        }
        public bool IsInLoadingScene
        {
            get
            {
                return this.isInLoadingScene;
            }
        }
        public static void loadSceneAsync(string sceneName, Action<int, string> loadSceneFinishHandler, List<PreLoadInfo> preLoad = null)
        {
            if (Instance.sceneName == sceneName)
            {
                onLoadScene(100);
                return;
            }
            string lastSceneName = Instance.sceneName;
            //打开loadingUI
            Instance.sceneName = sceneName;
            Instance.isInLoadingScene = true;
            Instance.preLoad = preLoad;
            Instance.loadSceneFinishHandler = loadSceneFinishHandler;
#if DEVELOP
            onSceneBundleFinish(100);
#else
            //把之前场景的资源卸载掉
            subSceneRef(lastSceneName);
            ResPool.Instance.loadSceneBundle(sceneName).ProgressHandler = onSceneBundleFinish;
            addSceneRef(sceneName);
#endif
        }
        private static void subSceneRef(string sceneName)
        {
            if (string.IsNullOrEmpty(sceneName)) return;
            string url = Instance.getAssetUrl(sceneName);
            string[] sceneDeps = getDeps(url);
            if (sceneDeps != null)
            {
                for (int i = 0; i < sceneDeps.Length; i++)
                {
                    subRef(sceneDeps[i]);
                }
            }
            subRef(url);
            //卸载掉bundle
            IAsyncBundle bundle = ResPool.Instance.getBundle(url);
            if (bundle != null)
            {
                ResPool.Instance.unloadBunle(bundle);
            }
        }

        private static void addSceneRef(string sceneName)
        {
            string url = Instance.getAssetUrl(sceneName);
            string[] sceneDeps = getDeps(url);
            if (sceneDeps != null)
            {
                for (int i = 0; i < sceneDeps.Length; i++)
                {
                    addRef(sceneDeps[i]);
                }
            }
            addRef(url);
        }

        private static void onSceneBundleFinish(int value)
        {
            if (value >= 100)
            {
                Loader.Instance.StartCoroutine(ResPool.Instance.loadSceneAsync(Instance.sceneName, onLoadScene, Instance.preLoad));
            }
            else
            {
                onLoadScene(0);
            }
        }

        private static void onLoadScene(int progress)
        {
            if (progress >= 100)
            {
                Instance.isInLoadingScene = false;
            }
            if (Instance.loadSceneFinishHandler != null)
            {
                Instance.loadSceneFinishHandler(progress, Instance.sceneName);
            }
        }

        /// <summary>
        /// 获取依赖
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static string[] getDeps(string bundleName)
        {
#if DEVELOP
            return null;
#else
            return Instance.getAllDeps(bundleName);
#endif
        }
        #endregion

    }
}
